package com.xiaoxiang.cameracontrol.zsocket;
import java.io.ByteArrayInputStream;
import java.io.IOException;

import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.HashMap;

/**
 *  提供一个基础数据类型的编/解码协议，以便在网络上进行byte类型的数据传输。<br>
 *  简单点说，就是将普通的int, float, String等类型转换成byte[]以便进行网络传输，
 *  然后在网络另一端将byte[]数据还原成原数据<br><br>
 *  支持的数据类型：<br> int(Integer), float(Float), byte[], String, String[], float[], HashMap<String, byte[]><br><br>
 *  将数据编码成byte[]时，使用Message.encode(original_data)函数<br><br>
 *  将数据从byte[]转换成原始数据时，使用Message.decode(byte_data)函数<br><br>
 * @author XiaoXiang
 *
 */

public class Message {
	private static final int TYPE_INT =1;
	private  static final int TYPE_STR =2;
	private  static final int TYPE_FLOAT =3;
	private  static final int TYPE_LIST =4;
	private  static final int TYPE_MAP =5;
	private  static final int TYPE_BYTE =6;
	
	/***
	 * 将int或Integer类型的数据转换成byte[]
	 * @param data int或Integer类型的数据
	 * @return 该数据对应的byte[]数据
	 */
	public static byte[] encode(int data) {
		byte[] head = FrameMessage.int_to_bytes(TYPE_INT, "litter");
		byte[] length = FrameMessage.int_to_bytes(8, "litter");
		byte[] content = int_to_byte8(data);
		return FrameMessage.att_list(head, FrameMessage.att_list(length, content));
	}
	
	/***
	 * 将String类型的数据转换成byte[]
	 * @param data String类型的数据
	 * @return 该数据对应的byte[]数据
	 */
	public static byte[] encode(String data) {
		try {
			byte[] head = FrameMessage.int_to_bytes(TYPE_STR, "litter");
			byte[] content = data.getBytes("UTF-8");
			byte[] length = FrameMessage.int_to_bytes(content.length, "litter");
			return FrameMessage.att_list(head, FrameMessage.att_list(length, content));
		} catch (UnsupportedEncodingException e) {
			return null;
		}
	}
	
	/***
	 * 将float或Float类型的数据转换成byte[]
	 * @param data float或Float类型的数据
	 * @return 该数据对应的byte[]数据
	 */
	public static byte[] encode(float data) {
		int ibits = Float.floatToIntBits(data);
		byte[] head = FrameMessage.int_to_bytes(TYPE_FLOAT, "litter");
		byte[] length = FrameMessage.int_to_bytes(8, "litter");
		byte[] content = int_to_byte8(ibits);
		return FrameMessage.att_list(head, FrameMessage.att_list(length, content));
	}
	
	/***
	 * 将byte[]类型的数据转换成byte[]
	 * @param data byte[]类型的数据
	 * @return 该数据转换后的byte[]数据
	 */
	public static byte[] encode(byte[] data) {
		byte[] head = FrameMessage.int_to_bytes(TYPE_BYTE, "litter");
		byte[] length = FrameMessage.int_to_bytes(data.length, "litter");
		byte[] content = data;
		return FrameMessage.att_list(head, FrameMessage.att_list(length, content));
	}
	
	/***
	 * 将HashMap<String, byte[]>类型的数据转换成byte[]
	 * @param data HashMap<String, byte[]>类型的数据
	 * @return 该数据对应的byte[]数据
	 */
	public static byte[] encode(HashMap<String, byte[]> data) {
		byte[] head = FrameMessage.int_to_bytes(TYPE_MAP, "litter");
		int data_length = 0;
		byte[] content = new byte[0];
		for(HashMap.Entry<String, byte[]> item : data.entrySet()) {
			byte[] key_content = encode(item.getKey());
			byte[] value_content = encode(item.getValue());
			
			byte[] item_content = FrameMessage.att_list(key_content, value_content);
			data_length+= item_content.length;
			content = FrameMessage.att_list(content, item_content);
		}
		return FrameMessage.att_list(head, FrameMessage.att_list(FrameMessage.int_to_bytes(data_length, "litter"), content));
	}
	
	/***
	 * 将数组类型的数据转换成byte[]，仅支持int[], float[], String[], Integer[], Float[], byte[]。如果是用了不支持的类型，则返回结果为null
	 * @param data 数组类型的数据
	 * @return 该数据对应的byte[]数据
	 */
	public static byte[] encode(Object[] data) {
		byte[] head = FrameMessage.int_to_bytes(TYPE_LIST, "litter");
		int data_length = 0;
		byte[] content = new byte[0];
		
		for(Object item : data) {
			byte[] item_content = null;
			if(item instanceof Integer) {
				item_content = encode((Integer)item);
			}else if(item instanceof Float) {
				item_content = encode((Float)item);
			}else if(item instanceof String) {
				item_content = encode((String)item);
			}else if(item instanceof byte[]){
				item_content = encode((byte[])item);
			}else {
				System.out.println("不支持数据格式: "+data.getClass()+" 编码为byte[]");
				return null;
			}
			data_length+= item_content.length;
			content = FrameMessage.att_list(content, item_content);
		}
		byte[] byte_length = FrameMessage.int_to_bytes(data_length, "litter");
		
		return FrameMessage.att_list(head, FrameMessage.att_list(byte_length, content));
		
	}
	
	/***
	 * 从输入流的开头读取一个数字
	 * @param in 读取数字的输入流
	 * @return 读取到的数字值，如果输入流中没有数据则返回-1
	 * @throws IOException
	 */
	private static int read_int(ByteArrayInputStream in) throws IOException {
		byte[] attr_buffer = new byte[4];
		int actual = in.read(attr_buffer);
		if(actual<=0) return -1;
		return FrameMessage.bytes_to_int(attr_buffer, "litter");
	}

	private static byte[] int_to_byte8(int data){
		byte[] temp = new byte[]{(byte)0, (byte)0, (byte)0, (byte)0};
		byte[] datas = FrameMessage.int_to_bytes(data, "litter");
		return FrameMessage.att_list(datas, temp);
	}
	
	public static Object decode(byte[] data) {
		if(data==null || data.length<8) return null;
		ByteArrayInputStream in = new ByteArrayInputStream(data);
		return decode_(in, data.length);
	}

	
	/***
	 * 将byte[]类型的数据还原成原数据。返回的类型可能有：<br> 
	 * int, byte[], float, String, int[], float[], String[], HashMap<br><br>
	 * 可以使用强制类型转换将其转换成对应的数据格式。
	 * @param in byte[]类型的数据
	 * @return 该数据对应的原数据
	 */
	private static Object decode_(ByteArrayInputStream in, int max_length) {
		try {
			int type = read_int(in);
			int length = read_int(in);
			switch(type) {
				case TYPE_INT:
					return decode_int(in, length);
				case TYPE_FLOAT:
					return decode_float(in, length);
				case TYPE_STR:
					return decode_string(in, length);
				case TYPE_LIST:
					return decode_list(in, length);
				case TYPE_BYTE:
					return decode_byte(in, length);
				case TYPE_MAP:
					return decode_map(in, length);
			}
		}catch(IOException e) {
			return null;
		}
		return null;
	}


	private static int decode_int(ByteArrayInputStream in, int max_length) throws IOException {
		byte[] data = new byte[max_length];
		if(in.read(data)<max_length) return -1;
		return FrameMessage.bytes_to_int(data, "litter");
	}
	
	
	private static float decode_float(ByteArrayInputStream in, int max_length) throws IOException {
		byte[] data = new byte[max_length];
		if(in.read(data)<max_length) return -1.0f;
		int s = FrameMessage.bytes_to_int(FrameMessage.sub_list(data, 0, 4), "litter");
		return Float.intBitsToFloat(s);
	}
	
	private  static String decode_string(ByteArrayInputStream in, int max_length) throws IOException {
		byte[] data = new byte[max_length];
		if(in.read(data)<max_length) return null;
		try {
			return new String(data, "UTF-8");
		} catch (UnsupportedEncodingException e) {
			return null;
		}
	}
	
	private static byte[] decode_byte(ByteArrayInputStream in, int max_length) throws IOException {
		byte[] data = new byte[max_length];
		if(in.read(data)<max_length) return null;
		return data;
	}
	
	private static Object decode_map(ByteArrayInputStream in, int max_length) {
		HashMap<String, byte[]> result = new HashMap<String, byte[]>();
		int readed_length = 0;
		do {
			try {
				int type = read_int(in);
				int length = read_int(in);
				String key = decode_string(in, length);
				readed_length = readed_length+8+length;
				
				type = read_int(in);
				length = read_int(in);
				byte[] value = decode_byte(in, length);
				result.put(key, value);
				readed_length = readed_length+8+length;
			}catch(IOException e) {
				return null;
			}
		}while(readed_length<max_length);
		return result;
	}
	
	private static Object decode_list(ByteArrayInputStream in, int max_length) {
		ArrayList<Object> result = new ArrayList<Object>();
		int readed_length = 0;
		do {
			try {
				int type = read_int(in);
				int length = read_int(in);
				switch(type) {
				case TYPE_INT:
					result.add(decode_int(in, length));
					break;
				case TYPE_FLOAT:
					result.add(decode_float(in, length));
					break;
				case TYPE_STR:
					result.add(decode_string(in, length));
					break;
				case TYPE_LIST:
					result.add(decode_list(in, length));
					break;
				case TYPE_BYTE:
					result.add(decode_byte(in, length));
					break;
				case TYPE_MAP:
					result.add(decode_map(in, length));
					break;
				}
				readed_length = readed_length+8+length;
			}catch(IOException e) {
				return null;
			}
		}while(readed_length<max_length);
		
		return result.toArray();
	}
	
	
	public static void main(String[] args) {
		//Message类编码和解码测试程序
		byte[] encoded_byte = null;
	
		// 测试编码和解码数组。（支持的数组类型包括Int数组，Float数组，字符串数组，byte数组）
		String[] strs = {"测试a", "测试b", "测试c", "测试d"};
		/** 开始编码*/
		encoded_byte = encode(strs);
		/** 开始解码 */
		Object[] decoded_strs = (Object[]) decode(encoded_byte);
		/** 输出解码结果 */
		for(Object obj: decoded_strs) {
			System.out.println((String)obj);
		}
		System.out.println("******************************");
		
		// 测试编解码HashMap（仅支持HashMap<String, byte[])类型，其中的byte[]可以是由其他类型编码而成的数据）
		HashMap<String, byte[]> hmd = new HashMap<String, byte[]>();
		hmd.put("keya", encode("测试字符串"));
		hmd.put("keyb", encode(2));
		hmd.put("keyc", encode(3.2544f));
		hmd.put("keyd", encode(new Integer[] {1, 2,3, 4}));
		/** 开始编码*/
		encoded_byte = encode(hmd);
		/** 开始解码 */
		HashMap<String, byte[]> decoded_map = (HashMap<String, byte[]>) decode(encoded_byte);
		/** 输出解码结果 */
		System.out.println(String.valueOf(decode(decoded_map.get("keya"))));
		System.out.println(String.valueOf(decode(decoded_map.get("keyb"))));
		System.out.println(String.valueOf(decode(decoded_map.get("keyc"))));
		Object[] d_list = (Object[])decode(decoded_map.get("keyd"));
		for(Object c: d_list) {
			System.out.println(String.valueOf((int)c));
		}
		System.out.println("******************************");
		
	}
}
